package references

import (
	"JVM/Jvmgo/GoToJVM/instructions/base"
	"JVM/Jvmgo/GoToJVM/runtimeData"
	"JVM/Jvmgo/GoToJVM/runtimeData/heap"
)

/*
该指令是从运行时常量池找到要复制的静态变量，然后为静态变量赋值
该指令需要完成两步：
 1. 找到静态变量 （操作数1，索引：来自字节码）
 2. 为静态变量赋值 （操作数2： 来自操作数栈弹出）
*/

// 为指定的静态域赋值
type PUT_STATIC struct {
	base.Index16Instruction
}

func (self *PUT_STATIC) Execute(frame *runtimeData.Frame) {
	currentMethod := frame.Method()
	currentClass := currentMethod.Class()
	cp := currentClass.ConstantPool()
	// 断言判断
	fieldRef := cp.GetConstant(self.Index).(*heap.FieldRef)
	field := fieldRef.ResolvedField()
	class := field.Class() // 拿到被使用的字段类，如果该类没有被初始化，就要先对该类进行初始化
	if !class.InitStarted() {
		frame.RevertNextPC()
		base.InitClass(frame.Thread(), class)
		return
	}
	// todo 完善类加载
	if !field.IsStatic() {
		panic("java.lang.IncompatibleClassChangeError")
	}
	// final 本质上是静态常量，在类初始化方法<clinit>中为其赋值，后续在实现<clinit>时完成该功能
	if field.IsFinal() {
		if currentClass != class || currentMethod.Name() != "<clinit>" {
			panic("java.lang.IllegalAccessError")
		}
	}

	descriptor := field.Descriptor()
	slotId := field.SlotId()
	slots := class.StaticVars()
	stack := frame.OperandStack()

	switch descriptor[0] {
	case 'Z', 'B', 'C', 'S', 'I':
		slots.SetInt(slotId, stack.PopInt())
	case 'F':
		slots.SetFloat(slotId, stack.PopFloat())
	case 'J':
		slots.SetLong(slotId, stack.PopLong())
	case 'D':
		slots.SetDouble(slotId, stack.PopDouble())
	case 'L', '[':
		slots.SetRef(slotId, stack.PopRef())
	}
}
